package com.zrf.gulimall.order.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.TypeReference;
import com.alipay.api.AlipayApiException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zrf.gulimall.commnon.core.exception.CustomException;
import com.zrf.gulimall.commnon.core.to.OrderTo;
import com.zrf.gulimall.commnon.core.utils.R;
import com.zrf.gulimall.commnon.core.vo.MemberResponseVo;
import com.zrf.gulimall.common.database.utils.PageUtils;
import com.zrf.gulimall.common.database.utils.Query;
import com.zrf.gulimall.order.config.AliPayTemplate;
import com.zrf.gulimall.order.constant.PayConstant;
import com.zrf.gulimall.order.dao.OrderDao;
import com.zrf.gulimall.order.entity.OrderEntity;
import com.zrf.gulimall.order.entity.OrderItemEntity;
import com.zrf.gulimall.order.entity.PaymentInfoEntity;
import com.zrf.gulimall.order.enume.OrderStatusEnum;
import com.zrf.gulimall.order.feign.CartFeignService;
import com.zrf.gulimall.order.feign.MemberFeignService;
import com.zrf.gulimall.order.feign.ProductFeignService;
import com.zrf.gulimall.order.feign.WmsFeignService;
import com.zrf.gulimall.order.interceptor.LoginInterceptor;
import com.zrf.gulimall.order.service.OrderItemService;
import com.zrf.gulimall.order.service.OrderService;
import com.zrf.gulimall.order.service.PaymentInfoService;
import com.zrf.gulimall.order.to.OrderCreateTo;
import com.zrf.gulimall.order.to.SpuInfoVo;
import com.zrf.gulimall.order.vo.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.zrf.gulimall.commnon.core.exception.StatusCode.NO_STOCK_EXCEPTION;
import static com.zrf.gulimall.order.constant.OrderConstant.USER_ORDER_TOKEN_PREFIX;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
	private ThreadLocal<OrderSubmitVo> confirmVoThreadLocal = new ThreadLocal<>();

	@Autowired
	private OrderItemService orderItemService;
	@Autowired
	private MemberFeignService memberFeignService;
	@Autowired
	private CartFeignService cartFeignService;
	@Autowired
	private WmsFeignService wmsFeignService;
	@Autowired
	private ProductFeignService productFeignService;
	@Autowired
	private AliPayTemplate aliPayTemplate;
	@Autowired
	private PaymentInfoService paymentInfoService;

	@Autowired
	private ThreadPoolExecutor executor;
	@Autowired
	private StringRedisTemplate redisTemplate;
	@Autowired
	private RabbitTemplate rabbitTemplate;

	@Override
	public PageUtils queryPage(Map<String, Object> params) {
		IPage<OrderEntity> page = this.page(
				new Query<OrderEntity>().getPage(params),
				new QueryWrapper<OrderEntity>()
		);

		return new PageUtils(page);
	}

	@Override
	public OrderConfirmVo confirmOrder() {
		OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
		// 获取当前登录的用户信息
		MemberResponseVo memberResponseVo = LoginInterceptor.LOGIN_USER.get();
		// 由于使用异步编排，导致其他线程无法获取当前线程的请求
		// 获取当前线程请求
		ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
			// 为当前线程设置请求
			RequestContextHolder.setRequestAttributes(requestAttributes);
			// 远程查询用户的收获地址
			List<MemberAddressVo> address = memberFeignService.getAddress(memberResponseVo.getId());
			orderConfirmVo.setMemberAddressVos(address);
		}, executor);
		CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
			// 为当前线程设置请求
			RequestContextHolder.setRequestAttributes(requestAttributes);
			// 远程查询用户勾选的购物项
			List<OrderItemVo> currentCartItems = cartFeignService.getCurrentCartItems();
			orderConfirmVo.setItems(currentCartItems);
		}, executor).thenRunAsync(() -> {
			List<OrderItemVo> items = orderConfirmVo.getItems();
			//获取全部商品的id
			List<Long> skuIds = items.stream()
					.map((OrderItemVo::getSkuId))
					.collect(Collectors.toList());
			//远程查询商品库存信息
			R skuHasStock = wmsFeignService.getSkuHasStock(skuIds);
			List<SkuStockVo> skuStockVos = skuHasStock.getData(new TypeReference<List<SkuStockVo>>() {
			});
			if (skuStockVos != null && skuStockVos.size() > 0) {
				//将skuStockVos集合转换为map
				Map<Long, Boolean> skuHasStockMap = skuStockVos.stream().collect(Collectors.toMap(SkuStockVo::getSkuId, SkuStockVo::getHasStock));
				orderConfirmVo.setStocks(skuHasStockMap);
			}
		}, executor);
		CompletableFuture.allOf(addressFuture, cartFuture).join();
		// 积分
		orderConfirmVo.setIntegration(memberResponseVo.getIntegration());
		// 防止重复提交，先生成一个token存到redis后给前端，前端提交订单时，带上这个token，后端在比较，一样就处理然后删除，否则就是重复提交
		String token = UUID.randomUUID().toString();
		redisTemplate.opsForValue().set(USER_ORDER_TOKEN_PREFIX + memberResponseVo.getId(), token, 30, TimeUnit.MINUTES);
		orderConfirmVo.setOrderToken(token);
		return orderConfirmVo;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) {
		SubmitOrderResponseVo submitOrderResponseVo = new SubmitOrderResponseVo();
		confirmVoThreadLocal.set(orderSubmitVo);
		// 获取当前登录的用户信息
		MemberResponseVo memberResponseVo = LoginInterceptor.LOGIN_USER.get();
		//1、验证令牌是否合法【令牌的对比和删除必须保证原子性】
		String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
		String orderToken = orderSubmitVo.getOrderToken();
		//通过lua脚本原子验证令牌和删除令牌
		Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
				Collections.singletonList(USER_ORDER_TOKEN_PREFIX + memberResponseVo.getId()),
				orderToken);
		if (Objects.isNull(result) || result == 0L) {
			submitOrderResponseVo.setCode(1);
			confirmVoThreadLocal.remove();
			return submitOrderResponseVo;
		}
		//1、创建订单、订单项等信息
		OrderCreateTo order = createOrder();
		//2、验证价格
//		BigDecimal payAmount = order.getOrder().getPayAmount();
//		BigDecimal payPrice = orderSubmitVo.getPayPrice();
		//金额对比
//		if (Math.abs(payAmount.subtract(payPrice).doubleValue()) < 0.01) {
		//3、保存订单
		saveOrder(order);
		//4、库存锁定,只要有异常，回滚订单数据
		WareSkuLockVo lockVo = new WareSkuLockVo();
		lockVo.setOrderSn(order.getOrder().getOrderSn());
		//获取出要锁定的商品数据信息
		List<OrderItemVo> orderItemVos = order.getOrderItems().stream().map((item) -> {
			OrderItemVo orderItemVo = new OrderItemVo();
			orderItemVo.setSkuId(item.getSkuId());
			orderItemVo.setCount(item.getSkuQuantity());
			orderItemVo.setTitle(item.getSkuName());
			return orderItemVo;
		}).collect(Collectors.toList());
		lockVo.setLocks(orderItemVos);
		// 调用远程锁定库存的方法
		//出现的问题：扣减库存成功了，但是由于网络原因超时，出现异常，导致订单事务回滚，库存事务不回滚(解决方案：seata)
		//为了保证高并发，不推荐使用seata，因为是加锁，并行化，提升不了效率,可以发消息给库存服务
		R r = wmsFeignService.orderLockStock(lockVo);
		if (r.getCode() == 0) {
			//锁定成功
			submitOrderResponseVo.setOrder(order.getOrder());
			// 订单创建成功，发送消息给MQ
			rabbitTemplate.convertAndSend("order-event-exchange", "order.create.order", order.getOrder());
			//删除购物车里的数据
			redisTemplate.delete("gulimall:cart:" + memberResponseVo.getId());
			confirmVoThreadLocal.remove();
			submitOrderResponseVo.setCode(0);
			return submitOrderResponseVo;
		} else {
			confirmVoThreadLocal.remove();
			//锁定失败
			throw new CustomException(NO_STOCK_EXCEPTION.getMsg(), NO_STOCK_EXCEPTION.getCode());
		}
	}

	@Override
	public OrderEntity getOrderByOrderSn(String orderSn) {
		return this.lambdaQuery()
				.eq(OrderEntity::getOrderSn, orderSn)
				.one();
	}

	@Override
	public void closeOrder(OrderEntity orderEntity) {
		//关闭订单之前先查询一下数据库，判断此订单状态是否已支付
		OrderEntity orderInfo = this.getOne(new QueryWrapper<OrderEntity>().
				eq("order_sn", orderEntity.getOrderSn()));
		if (orderInfo.getStatus().equals(OrderStatusEnum.CREATE_NEW.getCode())) {
			//代付款状态进行关单
			OrderEntity orderUpdate = new OrderEntity();
			orderUpdate.setId(orderInfo.getId());
			orderUpdate.setStatus(OrderStatusEnum.CANCLED.getCode());
			this.updateById(orderUpdate);
			// 发送消息给MQ
			OrderTo orderTo = new OrderTo();
			BeanUtils.copyProperties(orderInfo, orderTo);
			try {
				// 确保每个消息发送成功，给每个消息做好日志记录，(给数据库保存每一个详细信息)保存每个消息的详细信息
				rabbitTemplate.convertAndSend("order-event-exchange", "order.release.other", orderTo);
			} catch (Exception e) {
				//TODO 定期扫描数据库，重新发送失败的消息
			}
		}
	}

	@Override
	public String pay(String orderSn) throws AlipayApiException {
		OrderEntity orderEntity = this.getOrderByOrderSn(orderSn);
		PayVo payVo = new PayVo();
		payVo.setTotalAmount(orderEntity.getPayAmount().setScale(2, RoundingMode.HALF_UP).toString());
		payVo.setOutTradeNo(orderSn);
		List<OrderItemEntity> orderItemEntities = orderItemService.getByOrderSn(orderSn);
		OrderItemEntity orderItemEntity = orderItemEntities.stream().findFirst().orElse(new OrderItemEntity());
		payVo.setBody(orderItemEntity.getSkuAttrsVals());
		payVo.setSubject(orderItemEntity.getSkuName());
		return aliPayTemplate.pay(payVo);
	}

	@Override
	public PageUtils queryPageWithItem(Map<String, Object> params) {
		MemberResponseVo memberResponseVo = LoginInterceptor.LOGIN_USER.get();
		IPage<OrderEntity> page = this.lambdaQuery()
				.eq(OrderEntity::getMemberId, memberResponseVo.getId())
				.orderByDesc(OrderEntity::getCreateTime)
				.page(new Query<OrderEntity>().getPage(params));
		// 查询订单的订单项
		List<String> orderSnList = page.getRecords().stream().map(OrderEntity::getOrderSn).collect(Collectors.toList());
		List<OrderItemEntity> orderItemEntities = orderItemService.getByOrderSns(orderSnList);
		Map<String, List<OrderItemEntity>> itemMap = orderItemEntities.stream().collect(Collectors.groupingBy(OrderItemEntity::getOrderSn));
		// 为每个订单设置订单项
		page.getRecords().forEach(item -> {
			if (CollUtil.isNotEmpty(itemMap.get(item.getOrderSn()))) {
				item.setOrderItemEntityList(itemMap.get(item.getOrderSn()));
			}
		});
		return new PageUtils(page);
	}

	@Override
	public String handlePayResult(PayAsyncVo payAsyncVo) {
		//保存交易流水信息
		PaymentInfoEntity paymentInfo = new PaymentInfoEntity();
		paymentInfo.setOrderSn(payAsyncVo.getOut_trade_no());
		paymentInfo.setAlipayTradeNo(payAsyncVo.getTrade_no());
		paymentInfo.setTotalAmount(new BigDecimal(payAsyncVo.getBuyer_pay_amount()));
		paymentInfo.setSubject(payAsyncVo.getBody());
		paymentInfo.setPaymentStatus(payAsyncVo.getTrade_status());
		paymentInfo.setCreateTime(new Date());
//		paymentInfo.setCallbackTime(payAsyncVo.getNotify_time());
		//添加到数据库中
		paymentInfoService.save(paymentInfo);
		//修改订单状态
		//获取当前状态
		String tradeStatus = payAsyncVo.getTrade_status();
		if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
			//支付成功状态
			//获取订单号
			String orderSn = payAsyncVo.getOut_trade_no();
			this.baseMapper.updateOrderStatus(orderSn,OrderStatusEnum.PAYED.getCode(), PayConstant.ALIPAY);
		}
		return "success";
	}

	/**
	 * 保存订单和订单项
	 */
	private void saveOrder(OrderCreateTo orderTo) {
		//获取订单信息
		OrderEntity order = orderTo.getOrder();
		order.setModifyTime(new Date());
		order.setCreateTime(new Date());
		//保存订单
		this.save(order);
		//获取订单项信息
		List<OrderItemEntity> orderItems = orderTo.getOrderItems();
		//批量保存订单项数据
		orderItemService.saveBatch(orderItems);
	}

	/**
	 * 创建OrderCreateTo
	 */
	private OrderCreateTo createOrder() {
		OrderCreateTo createTo = new OrderCreateTo();
		//1、生成订单号
		String orderSn = IdWorker.getTimeId();
		OrderEntity orderEntity = builderOrder(orderSn);
		//2、获取到所有的订单项
		List<OrderItemEntity> orderItemEntities = builderOrderItems(orderSn);
		//3、验价(计算价格、积分等信息)
		computePrice(orderEntity, orderItemEntities);
		createTo.setOrder(orderEntity);
		createTo.setOrderItems(orderItemEntities);
		return createTo;
	}

	/**
	 * 构建订单数据
	 *
	 * @param orderSn
	 * @return
	 */
	private OrderEntity builderOrder(String orderSn) {
		//获取当前用户登录信息
		MemberResponseVo memberResponseVo = LoginInterceptor.LOGIN_USER.get();
		OrderEntity orderEntity = new OrderEntity();
		orderEntity.setMemberId(memberResponseVo.getId());
		orderEntity.setOrderSn(orderSn);
		orderEntity.setMemberUsername(memberResponseVo.getUsername());
		OrderSubmitVo orderSubmitVo = confirmVoThreadLocal.get();
		//远程获取收货地址和运费信息
		R fareAddressVo = wmsFeignService.getFare(orderSubmitVo.getAddrId());
		FareVo fareResp = fareAddressVo.getData("data", new TypeReference<FareVo>() {
		});
		//获取到运费信息
		BigDecimal fare = fareResp.getFare();
		orderEntity.setFreightAmount(fare);
		//获取到收货地址信息
		MemberAddressVo address = fareResp.getAddress();
		//设置收货人信息
		orderEntity.setReceiverName(address.getName());
		orderEntity.setReceiverPhone(address.getPhone());
		orderEntity.setReceiverPostCode(address.getPostCode());
		orderEntity.setReceiverProvince(address.getProvince());
		orderEntity.setReceiverCity(address.getCity());
		orderEntity.setReceiverRegion(address.getRegion());
		orderEntity.setReceiverDetailAddress(address.getDetailAddress());
		//设置订单相关的状态信息
		orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
		orderEntity.setAutoConfirmDay(7);
		orderEntity.setConfirmStatus(0);
		return orderEntity;
	}

	/**
	 * 构建所有订单项数据
	 */
	public List<OrderItemEntity> builderOrderItems(String orderSn) {

		List<OrderItemEntity> orderItemEntityList = new ArrayList<>();
		//最后确定每个购物项的价格
		List<OrderItemVo> currentCartItems = cartFeignService.getCurrentCartItems();
		if (currentCartItems != null && currentCartItems.size() > 0) {
			orderItemEntityList = currentCartItems.stream().map((items) -> {
				//构建订单项数据
				OrderItemEntity orderItemEntity = builderOrderItem(items);
				orderItemEntity.setOrderSn(orderSn);

				return orderItemEntity;
			}).collect(Collectors.toList());
		}
		return orderItemEntityList;
	}

	/**
	 * 构建某一个订单项的数据
	 *
	 * @param orderItem 订单项
	 */
	private OrderItemEntity builderOrderItem(OrderItemVo orderItem) {
		OrderItemEntity orderItemEntity = new OrderItemEntity();
		//1、商品的spu信息
		Long skuId = orderItem.getSkuId();
		//获取spu的信息
		R spuInfo = productFeignService.getSpuInfoBySkuId(skuId);
		SpuInfoVo spuInfoData = spuInfo.getData("data", new TypeReference<SpuInfoVo>() {
		});
		orderItemEntity.setSpuId(spuInfoData.getId());
		orderItemEntity.setSpuName(spuInfoData.getSpuName());
		orderItemEntity.setSpuBrand(spuInfoData.getBrandName());
		orderItemEntity.setCategoryId(spuInfoData.getCatalogId());
		//2、商品的sku信息
		orderItemEntity.setSkuId(skuId);
		orderItemEntity.setSkuName(orderItem.getTitle());
		orderItemEntity.setSkuPic(orderItem.getImage());
		orderItemEntity.setSkuPrice(orderItem.getPrice());
		orderItemEntity.setSkuQuantity(orderItem.getCount());
		//使用StringUtils.collectionToDelimitedString将list集合转换为String
		String skuAttrValues = StringUtils.collectionToDelimitedString(orderItem.getSkuAttrValues(), ";");
		orderItemEntity.setSkuAttrsVals(skuAttrValues);
		//3、商品的优惠信息

		//4、商品的积分信息
		orderItemEntity.setGiftGrowth(orderItem.getPrice().multiply(new BigDecimal(orderItem.getCount())).intValue());
		orderItemEntity.setGiftIntegration(orderItem.getPrice().multiply(new BigDecimal(orderItem.getCount())).intValue());

		//5、订单项的价格信息
		orderItemEntity.setPromotionAmount(BigDecimal.ZERO);
		orderItemEntity.setCouponAmount(BigDecimal.ZERO);
		orderItemEntity.setIntegrationAmount(BigDecimal.ZERO);
		//当前订单项的实际金额.总额 - 各种优惠价格
		//原来的价格
		BigDecimal origin = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity().toString()));
		//原价减去优惠价得到最终的价格
		BigDecimal subtract = origin.subtract(orderItemEntity.getCouponAmount())
				.subtract(orderItemEntity.getPromotionAmount())
				.subtract(orderItemEntity.getIntegrationAmount());
		orderItemEntity.setRealAmount(subtract);

		return orderItemEntity;
	}

	/**
	 * 计算价格的方法
	 *
	 * @param orderEntity       订单
	 * @param orderItemEntities 订单项
	 */
	private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> orderItemEntities) {
		//总价
		BigDecimal total = new BigDecimal("0.0");
		//优惠价
		BigDecimal coupon = new BigDecimal("0.0");
		BigDecimal intergration = new BigDecimal("0.0");
		BigDecimal promotion = new BigDecimal("0.0");
		//积分、成长值
		Integer integrationTotal = 0;
		Integer growthTotal = 0;
		//订单总额，叠加每一个订单项的总额信息
		for (OrderItemEntity orderItem : orderItemEntities) {
			//优惠价格信息
			coupon = coupon.add(orderItem.getCouponAmount());
			promotion = promotion.add(orderItem.getPromotionAmount());
			intergration = intergration.add(orderItem.getIntegrationAmount());
			//总价
			total = total.add(orderItem.getRealAmount());
			//积分信息和成长值信息
			integrationTotal += orderItem.getGiftIntegration();
			growthTotal += orderItem.getGiftGrowth();
		}
		//1、订单价格相关的
		orderEntity.setTotalAmount(total);
		//设置应付总额(总额+运费)
		orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));
		orderEntity.setCouponAmount(coupon);
		orderEntity.setPromotionAmount(promotion);
		orderEntity.setIntegrationAmount(intergration);
		//设置积分成长值信息
		orderEntity.setIntegration(integrationTotal);
		orderEntity.setGrowth(growthTotal);
		//设置删除状态(0-未删除，1-已删除)
		orderEntity.setDeleteStatus(0);
	}

}